3.3 同步
高IRQL的同步
在内核执行的各个阶段,内核必须保证在临界区中同一时刻只有一个处理器在执行,内核必须保证所有线程按照互斥的方法访问这些数据(内核临界区)。
由于中断的发生,该中断的处理例程需要修改一个全局的数据结构,与此同时,内核恰好在更新该数据结构,违背了互斥的原则(同一时间只有一个处理器访问数据结构)。
windows采用了一个比较复杂的方法:首先内核先将处理器的IRQL(中断请求级别)提高到能访问该数据结构的最高的级别,这样就屏蔽了在中断处理例程中使用该资源的中断。但是这种策略对于单处理器来说是可以的,但是不能阻止多处理器发生中断。
自旋锁
内核用来实现多处理器互斥的机制是自旋锁。自旋锁是一个与某个局部变量相关联的锁原语,在进入临界区之前,内核(处理器)会一直尝试获取相关联的自旋锁,直到成功为止。
自旋锁是通过硬件支持的test-and-set操作来实现的,也即:在一条原子指令内测试并获取锁,这样避免多线对锁的争夺。
每一个自旋锁都拥有一个IRQL,并且这个IRQL总是在DPC/Dispatch或者更高级别,因为这个原因,如果一个线程拥有自旋锁,则他永远不会被抢占。
任何一个试图获取自旋锁的处理器本质上都是处于忙等状态。因为处理器在进入临界区之前会循环的去获取自旋锁
利用pause汇编指令,插入到忙等的循环中,这条指令告诉处理器他正在执行的部分是自旋锁的获取循环部分。
排队的自旋锁
当一个处理器想要获取被其他处理器持有的自旋锁的时候,他会把自己的标志符放在与该自旋锁关联的队列中,如果当前正持有自旋锁的处理器释放的,则其将自旋锁移交给队列的第一个处理器,如果处理器正在等待一个忙着的自旋锁,他会检查处于该处理器之前的处理器的标志,而不是检查自旋锁的标志。说是排队的自旋锁,还不如说是排队等待自旋锁的处理器。
锁是留给内核使用的,所以,在设备驱动程序中获取这些排队的自旋锁是不被支持的。
栈内排队的自旋锁
自旋锁句柄是一个数据结构。包含了锁的所有权(要求局部变量的原因所在)和处理器队列等状态信息,由此,句柄不应该是一个全局变量,往往是一个栈变量,以保证调用线程的局部性。